package com.example.demo.game;

import com.example.demo.DemoApplication;
import com.example.demo.model.User;
import com.example.demo.model.UserMapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.UUID;

/**
 * @program: gobang
 * @author: JCM
 * @create: 2023-03-11 07:38
 * @description: 这个类用来表示一个房间
 **/
@Data
public class  Room {
    // 使用字符串类型表示，方便生成唯一值
    private String roomId;

    private User user1;
    private User user2;
    // 先手方玩家 id
    private int whiteUser;
    // 房间内聊天消息
    private String message;
    private static final int MAX_ROW = 15;
    private static final int MAX_COL = 15;

    private UserMapper userMapper;
    // 创建 ObjectMapper 来转换 JSON
    private ObjectMapper objectMapper = new ObjectMapper();
    //@Autowired
    private OnlineUserManager onlineUserManager;
    // 引入 RoomManage 用于房间销毁
    //@Autowired
    private RoomManager roomManager;
    // 棋盘
    // 使用 0 表示当前位置未落子
    // 使用 1 表示 user1 的棋子
    // 使用 2 表示 user2 的棋子
    private int board[][] = new int[MAX_ROW][MAX_COL];



    /**
     * 1. 记录当前落子的位置
     * 2. 进行胜负判定
     * 3. 给客户端返回响应
     * @param reqJson
     */
    public void putChess(String reqJson) throws IOException {
        // 1. 记录当前落子位置
        GameRequest request = objectMapper.readValue(reqJson,GameRequest.class);
        GameResponse response = new GameResponse();
        // 当前这个棋子是玩家1 下的还是玩家2，根据玩家来决定数组中填写 1 还是 2
        int chess = (request.getUserId() == user1.getUserId()) ? 1 : 2;
        int row = request.getRow();
        int col = request.getCol();
        if(board[row][col] != 0) {
            // 保险起见多判定一下
            System.out.println("当前位置"+"("+row+","+col+") 已经有棋子了！");
            return;
        }
        board[row][col] = chess;
        // 2. 打印棋盘信息，方便观察局势和后续判断胜负
        printBoard();

        // 3. 进行胜负判定
        int winner = checkWinner(row,col,chess);
        // 4. 给房间中的所有客户端返回响应
         response.setMessage("putChess");
         response.setUserId(request.getUserId());
         response.setRow(row);
         response.setCol(col);
         response.setWinner(winner);
         response.setState(0);

         // 要写给用户发送 websocket 数据，就需要获取到这个用户的 WebSocketSession
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(user2.getUserId());
        // 万一查到当前会话为空（玩家下线了）特殊处理一下
        if (session1 == null) {
            // 玩家1 下线了，直接认为 玩家2 获胜
            response.setWinner(user2.getUserId());
            System.out.println("玩家1 掉线");
        }
        if (session2 == null) {
            // 玩家2 下线了，直接认为 玩家1 获胜
            response.setWinner(user1.getUserId());
            System.out.println("玩家2 掉线");
        }

        // 把响应构造成 JSON 字符串，通过 session 进行传输
        String respJson = objectMapper.writeValueAsString(response);
        if (session1 != null) {
            session1.sendMessage(new TextMessage(respJson));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(respJson));
        }

        // 5. 如果当前胜负已分就可以销毁房间
        if (response.getWinner() != 0) {
            // 胜负已分
            System.out.println("游戏结束！房间即将销毁 roomId = " + roomId + "获胜方法");
            // 更新失败方和获胜方的信息
            int winUserId = response.getUserId();
            int loseUserId = response.getUserId() == user1.getUserId() ? user2.getUserId() : user1.getUserId();
            userMapper.userWin(winUserId);
            userMapper.userLose(loseUserId);
            // 销毁方法
            roomManager.remove(roomId,user1.getUserId(),user2.getUserId());
        }
    }

    // 更新消息
    public void updateMessage(String reqJson) throws IOException {
        GameRequest request = objectMapper.readValue(reqJson,GameRequest.class);
        GameResponse response = new GameResponse();
        // 确定信息发送方
        User sender = null;
        if(user1.getUserId() == request.getUserId()) {
            sender = user1;
        }else {
            sender = user2;
        }
        // 给房间中的用户返回响应
        response.setMessage(sender.getUsername()+":"+request.getMessage());
        response.setUserId(sender.getUserId());
        response.setState(1);

        // 要写给用户发送 websocket 数据，就需要获取到这个用户的 WebSocketSession
        WebSocketSession session1 = onlineUserManager.getFromGameRoom(user1.getUserId());
        WebSocketSession session2 = onlineUserManager.getFromGameRoom(user2.getUserId());
        // 把响应构造成 JSON 字符串，通过 session 进行传输
        String respJson = objectMapper.writeValueAsString(response);
        if (session1 != null) {
            session1.sendMessage(new TextMessage(respJson));
        }
        if (session2 != null) {
            session2.sendMessage(new TextMessage(respJson));
        }
    }

    private void printBoard() {
        // 打印出棋盘
        System.out.println("[打印棋盘信息]"+roomId);
        System.out.println("==================================================================");
        for (int r = 0; r < MAX_ROW; r++) {
            for (int c = 0; c < MAX_COL; c++) {
                System.out.print(board[r][c] + " ");
            }
            System.out.println();
        }
        System.out.println("==================================================================");

    }


    // 使用这个方法来判定当前棋子是否分出胜负
    // 1. 约定如果玩家1 获胜，就返回玩家1 的 userId
    // 2. 如果玩家2 获胜，就返回玩家2 的 userId
    // 如果胜负未分，就返回 0
    private int checkWinner(int row, int col,int chess) {
        // 1. 先检查所有的行
        // 玩家下的子必须和连成五子的棋子有关联才行
        for(int c = col-4; c <= col; c++) {
            try {
                if(board[row][c] == chess
                        && board[row][c+1] == chess
                        && board[row][c+2] == chess
                        && board[row][c+3] == chess
                        && board[row][c+4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                //System.out.println("下标越界！");
                continue;
            }

        }
        // 2. 检查所有的列
        for(int r = row-4; r <= row; r++) {
            try {
                if (board[r][col] == chess
                    && board[r+1][col] == chess
                    && board[r+2][col] == chess
                    && board[r+3][col] == chess
                    && board[r+4][col] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                //System.out.println("下标越界！");
                continue;
            }
        }
        // 3. 检查正对角线
        for(int r = row-4,c = col-4; r <= row && c <= col; r++,c++) {
            try {
                if (board[r][c] == chess
                    && board[r+1][c+1] == chess
                    && board[r+2][c+2] == chess
                    && board[r+3][c+3] == chess
                    && board[r+4][c+4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                //System.out.println("下标越界！");
                continue;
            }
        }
        // 4. 检查反对角线
        for (int r = row-4,c = col+4; r <= row && c >= col; r++,c--) {
            try {
                if(board[r][c] == chess
                        && board[r+1][c-1] == chess
                        && board[r+2][c-2] == chess
                        && board[r+3][c-3] == chess
                        && board[r+4][c-4] == chess) {
                    return chess == 1 ? user1.getUserId() : user2.getUserId();
                }
            }catch (ArrayIndexOutOfBoundsException e) {
                //System.out.println("下标越界！");
                continue;
            }
        }
        int count = 0;
        for (int r = 0; r < MAX_ROW; r++) {
            for (int c = 0; c < MAX_COL; c++) {
                if(board[r][c] == 0) {
                    count++;
                }
            }
        }
        if (count == 0) {
            // 返回 -1 表示和棋
            return -1;
        }
        // 胜负未分，直接返回0
        return 0;
    }

    public Room() {
        // 构造Room 的时候生成一个唯一的字符串来作为房间 id
        roomId = UUID.randomUUID().toString();
        // 通过入口类中记录的 context 来手动获取到前面的 RoomManage 和 OnlineUserManager
        onlineUserManager = DemoApplication.context.getBean(OnlineUserManager.class);
        roomManager = DemoApplication.context.getBean(RoomManager.class);
        userMapper = DemoApplication.context.getBean(UserMapper.class);
    }

    public static void main(String[] args) {
        Room room = new Room();
        System.out.println(room.roomId);
    }
}
